/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package www.jSerialPort;

import giovynet.nativelink.SerialPort;
import giovynet.serial.Baud;
import giovynet.serial.Com;
import giovynet.serial.Parameters;
import giovynet.serial.Buffer;
import giovynet.devices.ScanDevices;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.PostConstruct;
import javax.enterprise.context.SessionScoped;
import javax.faces.application.FacesMessage;
import javax.inject.Named;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.swing.Timer;
import www.enumerators.Bauds;
import www.enumerators.ByteSize;
import www.enumerators.Parity;
import www.enumerators.Ports;
import www.enumerators.StopBits;
import javax.faces.context.FacesContext;

/**
 *
 * @author D.P.F.
 */
@Named
@SessionScoped
public class ManagePort implements Serializable, Servlet {

    private Com port;
    private Parameters parameters;
    private SerialPort strSerialPort;
    
    private String strPort;
    private int characterToStartReading;
    private boolean returnCompleteBuffer = false;
    private List<String> availableLstPorts;
    private String commandToSend;
    private String data;
    private Timer timerReadData;
    
    private String defaultBaud = "_9600";
    private String defaultPort = "COM3";
    private String bufferFormat = "SNNNNNNNSKGC"; //Representa la trama completa que el dispositivo (báscula) arroja al puerto serial
    private String subBuffer = "SNNNNNNN"; //Representa la subTrama que deseamos obtener de la trama completa

    public ManagePort() {
        System.out.println(">> ManagePort");
//        tryLoadLibreries();
//        setPortParameters();
//        this.obtainAvailablePorts();
    }

    @PostConstruct
    public void initManagePort() {
        System.out.println(">> initManagePort");
        tryLoadLibreries();
        setPortParameters();
        obtainAvailablePorts();
        setPortWhitOpenOne();
    }

    /**
     * Método para cargar las librerías nativas
     */
    public void tryLoadLibreries() {
        try {
            System.out.println(">> Attend to load Library..");
            System.load("C:/LibJavaSerial/NativeLibraries/libSerialPort.dll");
            System.out.println("Library has been loaded..");
        } catch (UnsatisfiedLinkError ule) {
            System.out.println("  >>Exception - Library already loaded..");
            //this.obtainAvailablePorts();
        }
    }

    /**
     * Método para setear los parámetros por defecto del puerto
     */
    public void setPortParameters() {
        System.out.println(">> setPortParameters");
        try {
            if (this.parameters == null) {
                this.parameters = new Parameters();
                this.parameters.setBaudRate(Baud.valueOf(defaultBaud));
                this.parameters.setByteSize("8");
                //this.parameters.setMinDelayRead(10);
                this.parameters.setMinDelayRead(0);
                this.parameters.setMinDelayWrite(0);
                this.parameters.setParity(Parity.N.getValue());
                //this.parameters.setPort("COM99");
                this.parameters.setReadInterval(5); //50
                this.parameters.setReadTotalConstant(40); //50, 100
                this.parameters.setStopBits(StopBits._1.getValue());
                this.parameters.setWriteTotalConstant(50);
                this.parameters.setWriteTotalMultiplier(10);
                System.out.println("  >> parametersCreated");
            }
        } catch (Exception ex) {
            Logger.getLogger(ManagePort.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * Método para configurar el puerto serial
     *
     * @param nombrePuerto
     * @param bauds
     * @param parity
     * @param bitsPerCaracter
     * @param stopBits
     * @throws Exception
     */
    public void ConfigurePort(String portName, String bauds, String parity, String bitsPerCaracter, String stopBits) throws Exception {
        parameters.setPort(portName);
        parameters.setBaudRate(Baud.valueOf(bauds));
        parameters.setParity(parity);
        parameters.setByteSize(bitsPerCaracter);
        parameters.setStopBits(stopBits);
    }

    /**
     * Método preámbulo para abrir el puerto serial seleccionado
     */
    public void handleOpenPort() {
        SerialPort _serialPort = new SerialPort();
        List<String> lstPorts = new ArrayList<String>();//availableLstPorts;
        for (int i = 0; i < availableLstPorts.size(); i++) {
            lstPorts.add(availableLstPorts.get(i));
        }

        lstPorts.remove(parameters.getPort());
        for (int i = 0; i < lstPorts.size(); i++) {
            if (!lstPorts.get(i).equals("COM1")) {
                if (validateSerialPortIsOpen(lstPorts.get(i))) {
                    System.out.println(">> Puerto" + lstPorts.get(i) + " cerrado..");
                    _serialPort.closePortC(lstPorts.get(i));
                }
            }
        }

        System.out.println(">> " + parameters.getPort() + " Status Before: " + _serialPort.getStateSerialPortC(parameters.getPort()));
        try {
            OpenPort();
        } catch (Exception ex) {
            Logger.getLogger(ManagePort.class.getName()).log(Level.SEVERE, null, ex);
        }
        System.out.println(">> " + parameters.getPort() + " Status After: " + _serialPort.getStateSerialPortC(parameters.getPort()));

    }

    /**
     * Método para abrir el port serial
     * @throws Exception
     */
    public void OpenPort() throws Exception {
        //Determinal el caracter final de la lectura
        characterToStartReading = 0;
        if (bufferFormat != null) {
            if (subBuffer == null) {
                subBuffer = bufferFormat;
            }
            characterToStartReading = bufferFormat.indexOf(subBuffer);
        } else {
            bufferFormat = "";
            returnCompleteBuffer = true;
        }
        try {
            if (bufferFormat.length() > 0) {
                //port = bufferFormat.length();
            }
            port = new Com(parameters);
            if(validateSerialPortIsOpen(parameters.getPort())){
                FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, "Aviso", "El puerto " + parameters.getPort() + " ha sido abierto"));
            } else {
                FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Aviso", "El puerto " + parameters.getPort() + " no se pudo abrir"));
            }
        } catch (Exception ex) {
            throw ex;
        }
    }

    /**
     * Método preámbulo para cerrar el puerto serial seleccionado
     */
    public void handleClosePort() {
        System.out.println(">> handleClosePort");
        try {
            this.ClosePort();
        } catch (Exception ex) {
            Logger.getLogger(ManagePort.class.getName()).log(Level.SEVERE, null, ex);
        }
        System.out.println("  >>" + "(" + parameters.getPort() + "): " + strSerialPort.getStateSerialPortC(parameters.getPort()));
    }

    /**
     * Método para cerrar el port serial
     * @throws Exception
     */
    public void ClosePort() throws Exception {
        try {
            port.close();
            if(!validateSerialPortIsOpen(parameters.getPort())){
                FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, "Aviso", "El puerto " + parameters.getPort() + " ha sido cerrado"));
            } else {
                FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Aviso", "El puerto " + parameters.getPort() + " no ha podido cerrarse"));
            }
            
        } catch (Exception ex) {
            //Logger.getLogger(PuertoObj.class.getName()).log(Level.SEVERE, null, ex);
            throw ex;
        }
    }

    /**
     * Método para inicializar el timerReadData y empezar la lectura cíclica
     * desde el puerto serial
     * @param timerDelay, intervalo de tiempo en milisegundos
     */
    public void startReadingData(int timerDelay) {
        System.out.println(">> startReadingData");
        if (timerReadData != null) {
            if (!timerReadData.isRunning()) {
                timerReadData = new Timer(timerDelay, new ActionListener() {
                    public void actionPerformed(ActionEvent e) {
                        ReadData();
                    }
                });
                timerReadData.start();
                FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, "Aviso", "Se ha iniciado la lectura cíclica de datos"));
                System.out.println("  >> timerReadData.start();");
            }
        } else {
            timerReadData = new Timer(timerDelay, new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    ReadData();
                }
            });
            timerReadData.start();
            FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, "Aviso", "Se ha iniciado la lectura cíclica de datos"));
            System.out.println("  >> timerReadData.start() From0;");
        }    
    }

    /**
     * Método para parar el timerReadData
     */
    public void stopReadingData() {
        System.out.println(">> stopReadingData");
        if (timerReadData != null) {
            if (timerReadData.isRunning()) {
                timerReadData.stop();
                FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, "Aviso", "Se ha parado la lectura cíclica de datos"));
                System.out.println("   >> timerReadData.stop();");
            }
        }

    }

    /**
     * Añade el character ascii 10 (salto de linea) a la cadena a enviar
     * @param command
     * @param entero
     * @return
     */
    public String addAsciiToString(String command, int entero) {
        //System.out.println("Comando a enviar: " + command);
        command = command + AsciiDecToChar(entero);
        //System.out.println("Comando a enviar after: " + command);
        return command;
    }

    /**
     * Convierte un entero a un caracter ascci
     * @param leido
     * @return
     */
    public char AsciiDecToChar(int leido) {
        return (char) leido;
    }

    public void handleWriteData(String command) {
        System.out.println(">> Comando a enviar: " + command);
        command = addAsciiToString(command, 10);//command + AsciiDecToChar(10);
        //System.out.println("Comando a enviar after: " + command);
        try {
            this.WriteData(command);
        } catch (Exception ex) {
            System.out.println("-- Exception WriteData");
            Logger.getLogger(ManagePort.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * Método para escribir comandos en el puerto
     * @param comando
     * @throws Exception
     */
    public void WriteData(String command) throws Exception {
        try {
            port.sendArrayChar(command.toCharArray());
        } catch (Exception ex) {
            System.out.println("Error en escritura");
        }
    }

    /**
     * Método para leer datos del puerto serial
     * @return
     */
    public String ReadData() {
        if (!validateSerialPortIsOpen(parameters.getPort())) { //Si el puerto esta cerrado
            System.out.println(">> El puerto esta cerrado");
            this.data = "Port Closed!!";
            return data;
        } else {
            //this.data = "";
            try {
//                SerialPort sp = new SerialPort();
//                sp.
                String dt = port.receiveToString(bufferFormat.length());
                System.out.println("Dato 0: " + "(" + dt + "), " + dt.length());
                if (dt == null || dt.trim().compareTo("") == 0) {
                    //data = "No hay datos";
                }
                if (!returnCompleteBuffer) {
                    if (dt.substring(characterToStartReading, subBuffer.length()).trim().length() == subBuffer.length()) {
                        data = dt.substring(characterToStartReading, subBuffer.length());
                    }
//                    data = dt.substring(characterToStartReading, subBuffer.length());
                } else {
                    data = dt;
                }
                System.out.println(">> El puerto esta abierto: " + data);
            } catch (Exception ex) {
                System.out.println("Error en escritura");
                //Logger.getLogger(ManagePort.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        return data;
    }

    //NotUsed
    public List<String> obtainAvailablePorts2() {
        System.out.println(">> obtainAvailablePorts");
        try {
            strSerialPort = new SerialPort();
        } catch (java.lang.UnsatisfiedLinkError ule) {
            System.out.println("-- UnsatisfiedLinkError");
            //System.
            return availableLstPorts;//Arrays.asList("No ports founded..");
        }

        if (availableLstPorts == null) {
            availableLstPorts = new ArrayList<String>();
        } else if (availableLstPorts.isEmpty()) {
            availableLstPorts = new ArrayList<String>();
        }

        //availableLstPorts = addAll(Arrays.asList( Ports.COM1.value, Ports.COM2.value, Ports.COM3.value, Ports.COM4.value, Ports.COM5.value, Ports.COM6.value, Ports.COM7.value, Ports.COM8.value, Ports.COM9.value, Ports.COM10.value, Ports.COM11.value, Ports.COM12.value ));
        availableLstPorts = Ports.COM1.catchAllValues();
        //setPortWhitOpenOne();
        System.out.println("  >> Ports: " + availableLstPorts);
        return availableLstPorts;
    }

    /**
     * Método para setear al puerto seleccionado con el puerto que esté abierto al iniciar la aplicación,
     * si no hay ningún puerto abierto, lo setea con el puerto por defecto
     */
    public void setPortWhitOpenOne() {
        System.out.println(">> setPortWhitOpenOne");
        boolean hasBeenSet = false;
        for (int i = 0; i < availableLstPorts.size() && !hasBeenSet; i++) {
            if (!availableLstPorts.get(i).equals("COM1")) {
                if (validateSerialPortIsOpen(availableLstPorts.get(i))) { //strSerialPort.getStateSerialPortC(availableLstPorts.get(i)).equals("free"))
                    this.parameters.setPort(availableLstPorts.get(i));
                    hasBeenSet = true;
                }
            }
        }
        if (this.parameters.getPort() == null || !hasBeenSet) {
            System.out.println("  >> setPortWhitDefault");
            parameters.setPort(this.defaultPort);
        }
        System.out.println("  >> portHasBeenSet: " + parameters.getPort());
    }

    /**
     * Método para obtener la lista de puertos seriales disponibles
     * @return List<String>, lista de puertos disponibles más el puerto por defecto (puerto del dispositivo)
     */
    public List<String> obtainAvailablePorts() {
        System.out.println(">> obtainAvailablePorts");
        if (availableLstPorts == null) {
            availableLstPorts = new ArrayList<String>();
        } else if (availableLstPorts.isEmpty()) {
            availableLstPorts = new ArrayList<String>();
        }

//       SerialPort serialPort = new SerialPort();       
        try {
            try {
                strSerialPort = new SerialPort();
                //availableLstPorts = strSerialPort.getFreeSerialPort();                
                if (parameters != null) {
                    System.out.println("AAA: " + validateSerialPortIsOpen(parameters.getPort()));
                    availableLstPorts = strSerialPort.getFreeSerialPort(); //Obtiene los puertos disponibles que esten cerrados
                    if (validateSerialPortIsOpen(parameters.getPort())) {
                        availableLstPorts.add(parameters.getPort()); //Si el puerto seleccionado está abierto, lo añadimos a la lista de puertos
                    } else {
                        System.out.println("BBB: " + parameters.getPort());
                    }
                } else {
                    System.out.println("CCC");
                    availableLstPorts = strSerialPort.getFreeSerialPort();
                }

                if(!availableLstPorts.contains(defaultPort)){
                    System.out.println("   >>añadidoDefault: " + defaultPort);
                    availableLstPorts.add(defaultPort);                    
                }
                System.out.println("   >>puertos: " + availableLstPorts);
                return availableLstPorts;
            } catch (UnsatisfiedLinkError ule) {
                System.out.println("-- UnsatisfiedLinkError");
                //System.
                return availableLstPorts;//Arrays.asList("No ports founded..");
            }
        } catch (Exception ex) {
            System.out.println("-- Exception");
            //Logger.getLogger(ManagePort.class.getName()).log(Level.SEVERE, null, ex);
            return availableLstPorts;//Arrays.asList("No ports founded..");
        }
    }

    /**
     * Verifica si el puerto está abierto
     * @param strCom
     * @return true, si el puerto esta abierto
     */
    public boolean validateSerialPortIsOpen(String strCom) {
        try {
            return !strSerialPort.getStateSerialPortC(strCom).equals("free");
        } catch (UnsatisfiedLinkError ule) {
            return false;
        }
    }

    // A V A I L A B L E S
    /**
     * Método para obtener los Baudios Disponibles
     * @return List<Baud>
     */
    public List<Bauds> availableLstBaud() {
        return Arrays.asList(Bauds._9600, Bauds._4800, Bauds._2400, Bauds._1200);
    }
//    public List<Baud> availableLstBaud() {
//        return Arrays.asList(Baud._9600, Baud._4800, Baud._2400, Baud._1200);
//    }

    /**
     * Método para obtener los tamaños de datos disponibles
     * @return List<ByteSize>
     */
    public List<ByteSize> availableLstByteSize() {
        return Arrays.asList(ByteSize._7, ByteSize._8);
    }

    /**
     * Método para obtener los Bits de parada Disponibles
     * @return List<StopBits>
     */
    public List<StopBits> availableLstStopBits() {
        return Arrays.asList(StopBits._1, StopBits._2);
    }

    public List<Parity> availableLstParity() {
        return Arrays.asList(Parity.N, Parity.O, Parity.S, Parity.E);
    }

    // E N C A P S U L A M I E N T O
    public Com getPort() {
        return port;
    }

    public void setPort(Com port) {
        this.port = port;
    }

    public String getBufferFormat() {
        return bufferFormat;
    }

    public void setBufferFormat(String bufferFormat) {
        this.bufferFormat = bufferFormat;
    }

    public String getSubBuffer() {
        return subBuffer;
    }

    public void setSubBuffer(String subBuffer) {
        this.subBuffer = subBuffer;
    }

    public int getCharacterToStartReading() {
        return characterToStartReading;
    }

    public void setCharacterToStartReading(int characterToStartReading) {
        this.characterToStartReading = characterToStartReading;
    }

    public boolean isReturnCompleteBuffer() {
        return returnCompleteBuffer;
    }

    public void setReturnCompleteBuffer(boolean returnCompleteBuffer) {
        this.returnCompleteBuffer = returnCompleteBuffer;
    }

    public Parameters getParameters() {
        return parameters;
    }

    public void setParameters(Parameters parameters) {
        this.parameters = parameters;
    }

    public String getStrPort() {
        return strPort;
    }

    public void setStrPort(String strPort) {
        this.strPort = strPort;
    }

    public List<String> getAvailableLstPorts() {
        return availableLstPorts;
    }

    public void setAvailableLstPorts(List<String> availableLstPorts) {
        this.availableLstPorts = availableLstPorts;
    }

    public void printParameters() {
        this.parameters.setBaudRate(Baud.valueOf(defaultBaud));
        try{
        System.out.println("Parameters: " + this.parameters.getPort() + "(" + strSerialPort.getStateSerialPortC(parameters.getPort()) + "), " + this.parameters.getBaudRate() + ", " + this.parameters.getParity() + ", " + this.parameters.getByteSize() + ", " + this.parameters.getStopBits());
        }catch(UnsatisfiedLinkError ule){
            System.out.println("  >> Exception printParameters UnsatisfiedLinkError");
        }
    }

    public String getStrBaud() {
        return defaultBaud;
    }

    public void setStrBaud(String _strBaud) {
        this.defaultBaud = _strBaud;
    }

    public String getCommandToSend() {
        return commandToSend;
    }

    public void setCommandToSend(String commandToSend) {
        this.commandToSend = commandToSend;
    }

    public String getData() {
        return data;
    }

    public void setData(String data) {
        this.data = data;
    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public ServletConfig getServletConfig() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public String getServletInfo() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public void destroy() {
        throw new UnsupportedOperationException("Not supported yet.");
    }
}
